<?php

namespace App\Repositories;

use App\Models\Project;
use App\Services\Git;
use Illuminate\Support\Facades\Auth;
use App\Services\Deploy;

/**
 * 项目配置仓库
 */
class ProjectRepository
{
    public static $CONF;

    /**
     * 获取列表数据
     * @return array
     */
    public function getPageList()
    {
        $name = request('name', '');

        $project = new Project();

        //相关用户项目数据
        $project = $project->where("user_id", Auth::id());

        // 项目名称搜索
        if ($name) {
            $project = $project->where('name', 'like', "%" . $name . "%");
        }

        return $project->paginate(config("global.paginate"));
    }


    /**
     * 获取所有可用项目配置
     * @return mixed
     */
    public function getActiveProjects()
    {
        $project = new Project();
        $project = $project->where('status', config("constants.project.status.active"));
        return $project->get();
    }

    /**
     * 添加项目配置
     * @param $request
     * @return bool
     */
    public function store($request)
    {
        $project = new Project();
        $project->user_id = Auth::id();
        if ($project->fill($request->all())->save()) {
            try {
                $project = $project->toArray();
                // 宿主机配置检查(1、权限 2、git)
                $result = Deploy::CheckHost($project);
                if ($result["flag"]) {
                    flash(trans('alerts.project.created_success'))->success();
                    return true;
                } else {
                    throw new \Exception(trans('alerts.project.created_error') . "<br>" . join("<br>", $result["log"]));
                }
            } catch (\Exception $e) {
                Project::destroy($project->id);
                flash($e->getMessage())->error();
                return false;
            }
        }
        flash(trans('alerts.project.created_error'))->error();
        return false;
    }

    /**
     * 修改项目配置视图
     * @param $id
     * @return array
     */
    public function edit($id)
    {
        $project = Project::find($id);
        if ($project) {
            return $project;
        }
        abort(404);
    }

    /**
     * 修改项目配置资料
     * @param $request
     * @param $id
     * @return bool
     */
    public function update($request, $id)
    {
        $project = Project::find($id);
        if ($project) {
            if ($project->fill($request->all())->save()) {
                flash(trans('alerts.project.updated_success'))->success();
                return true;
            }
            flash(trans('alerts.project.updated_error'))->error();
            return false;
        }
        abort(404);
    }

    /**
     * 删除角色
     * @param $id
     * @return bool
     */
    public function destroy($id)
    {
        $isDelete = Project::destroy($id);
        if ($isDelete) {
            flash(trans('alerts.project.deleted_success'))->success();
            return true;
        }
        flash(trans('alerts.project.deleted_error'))->error();
        return false;
    }

    /**
     * 获取当前进程的项目配置
     * @param null $id
     * @return mixed
     * @throws \Exception
     */
    public static function getConf($id = null)
    {
        if (empty(static::$CONF)) {
            static::$CONF = Project::find($id)->toArray();

            //先更新获取最新配置
            $revision = new Git(static::$CONF);
            if ($revision->updateRepo('master', ProjectRepository::_getDeployFromDir(static::$CONF))) {
                $projectConfig = static::_checkProjectConfig(static::$CONF);
                static::$CONF = array_merge(static::$CONF, $projectConfig);
                static::$CONF["deploy_from"] = config("app.deploy_from");
                static::$CONF["keep_version_num"] = config("app.keep_version_num");
            } else {
                throw new \Exception(trans('alerts.deploy.update_code_error'));
            }
        }
        return static::$CONF;
    }

    /**
     * 检查项目配置文件
     * @param $project
     * @return array
     * @throws \Exception
     */
    private static function _checkProjectConfig($project)
    {
        $result = ["excludes" => "", "release_user" => "", "release_to" => "", "release_library" => "", "hosts" => []];
        $deployFromDir = static::_getDeployFromDir($project);
        $projectServerConfig = $deployFromDir . "/" . config("app.project_server_config");
        $deployIgnoreConfig = $deployFromDir . "/" . config("app.deploy_ignore");
        if (!file_exists($projectServerConfig)) {
            throw new \Exception(trans("alerts.deploy.unknown_project_server"));
        } else {
            //项目部署服务器配置检查
            $projectServerConfig = spyc_load_file($projectServerConfig);
            if (empty($projectServerConfig)) {
                throw new \Exception(trans("alerts.deploy.project_server_error"));
            }
            if (empty($projectServerConfig[trans("strings.project.level_en." . $project["level"])])) {
                throw new \Exception(trans("alerts.deploy.project_server_error"));
            } else {
                $levelConfig = $projectServerConfig[trans("strings.project.level_en." . $project["level"])];
                if (empty($levelConfig["user"])) {
                    throw new \Exception(langFmt(trans("alerts.deploy.project_server_error"), [
                        'config' => "user",
                    ]));
                } else if (empty($levelConfig["release"])) {
                    throw new \Exception(langFmt(trans("alerts.deploy.project_server_error"), [
                        'config' => "release",
                    ]));
                } else if (empty($levelConfig["library"])) {
                    throw new \Exception(langFmt(trans("alerts.deploy.project_server_error"), [
                        'config' => "library",
                    ]));
                } else if (empty($levelConfig["hosts"]) || checkIP($levelConfig["hosts"]) == false) {
                    throw new \Exception(langFmt(trans("alerts.deploy.project_server_error"), [
                        'config' => "hosts",
                    ]));
                } else {
                    // 路径必须为绝对路径
                    $needAbsoluteDir = [
                        trans("labels.project.deploy_from") => config("app.deploy_from"),
                        trans("labels.project.webroot") => $levelConfig["release"],
                        trans("labels.project.releases") => $levelConfig["library"],
                    ];
                    foreach ($needAbsoluteDir as $tips => $dir) {
                        if (0 !== strpos($dir, '/')) {
                            throw new \Exception(langFmt(trans("alerts.deploy.config_dir_must_absolute"), [
                                'path' => sprintf('%s:%s', $tips, $dir),
                            ]));
                        }
                    }

                    $result["release_user"] = $levelConfig["user"];
                    $result["release_to"] = $levelConfig["release"];
                    $result["release_library"] = $levelConfig["library"];
                    $result["hosts"] = arr2str($levelConfig["hosts"], PHP_EOL);
                    $result["pre_deploy"] = arr2str($levelConfig["pre_deploy"], PHP_EOL);
                    $result["post_deploy"] = arr2str($levelConfig["post_deploy"], PHP_EOL);
                    $result["pre_release"] = arr2str($levelConfig["pre_release"], PHP_EOL);
                    $result["post_release"] = arr2str($levelConfig["post_release"], PHP_EOL);
                }
            }

            //忽略配置检查
            $deployIgnoreConfig = file_get_contents($deployIgnoreConfig);
            $result["excludes"] = empty($deployIgnoreConfig) ? "" : $deployIgnoreConfig;
        }
        return $result;
    }

    /**
     * 拼接宿主机的仓库目录
     * {deploy_from}/{env}/{project}
     * @return string
     */
    public static function getDeployFromDir()
    {
        return static::_getDeployFromDir(static::$CONF);
    }

    /**
     * 拼接宿主机的仓库目录
     * {deploy_from}/{env}/{project}
     * @param $project
     * @return string
     */
    public static function _getDeployFromDir($project)
    {
        $from = config("app.deploy_from");
        $env = trans("strings.project.level_en." . $project["level"]);
        !isset($env) && $env = 'unknow';
        $project = static::getGitProjectName($project["repo_url"]);
        return sprintf("%s/%s/%s", rtrim($from, '/'), rtrim($env, '/'), $project);
    }

    /**
     * 根据git地址获取项目名字
     * @param $gitUrl
     * @return mixed
     */
    public static function getGitProjectName($gitUrl)
    {
        if (preg_match('#.*/(.*?)\.git#', $gitUrl, $match)) {
            return $match[1];
        }
        return basename($gitUrl);
    }

    /**
     * 拼接宿主机的部署隔离工作空间
     * {deploy_from}/{env}/{project}-YYmmdd-HHiiss
     * @param $version
     * @return string
     */
    public static function getDeployWorkspace($version)
    {
        $from = static::$CONF["deploy_from"];
        $env = trans("strings.project.level_en." . static::$CONF["level"]);
        !isset($env) && $env = 'unknow';
        $project = static::getGitProjectName(static::$CONF["repo_url"]);

        return sprintf("%s/%s/%s-%s", rtrim($from, '/'), rtrim($env, '/'), $project, $version);
    }

    /**
     * 获取目标机要发布的目录
     * {webroot}
     * @return string
     */
    public static function getTargetWorkspace()
    {
        return rtrim(static::$CONF["release_to"], '/');
    }

    /**
     * 拼接目标机要发布的目录
     * {release_library}/{project}/{version}
     *
     * @param $version
     * @return string
     */
    public static function getReleaseVersionDir($version = '')
    {
        return sprintf('%s/%s/%s', rtrim(static::$CONF["release_library"], '/'),
            static::getGitProjectName(static::$CONF["repo_url"]), $version);
    }

    /**
     * 拼接目标机要发布的打包文件路径
     * {release_library}/{project}/{version}.tar.gz
     *
     * @param string $version
     * @return string
     */
    public static function getReleaseVersionPackage($version = '')
    {
        return sprintf('%s.tar.gz', static::getReleaseVersionDir($version));
    }

    /**
     * 获取当前进程配置的目标机器host列表
     * @return array
     */
    public static function getHosts()
    {
        return str2arr(static::$CONF["hosts"]);
    }

    /**
     * 获取宿主机tar文件路径
     * {deploy_from}/{env}/{project}-YYmmdd-HHiiss.tar.gz
     * @param $version
     * @return string
     */
    public static function getDeployPackagePath($version)
    {
        return sprintf('%s.tar.gz', static::getDeployWorkspace($version));
    }
}